﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WhittleList.Tests.Mocks;
using WhittleList.Controllers;
using System.Web.Mvc;
using WhittleList.Bll;
using WhittleList.Tests.Helpers;
using WhittleList.Bll.Exceptions;

namespace WhittleList.Tests.Controllers
{
    [TestClass]
    public class SectionControllerTests
    {
        private List<FakeSite> siteData;
        private List<FakeProject> projectData;
        private List<FakeSection> sectionData;
        private FakeSiteRepository siteRepository;
        private FakeUserRepository userRepository;
        private FakeHttpContext httpContext;
        private SectionController controller;
        private Random rnd;


        [TestInitialize]
        public void Init()
        {
            rnd = new Random();
            siteData = FakeSite.GetList(2);
            projectData = FakeProject.GetList(10, rnd);
            sectionData = FakeSection.GetList(15, rnd);

            siteData[0].SetProjects(projectData.Take(8).ToList());
            siteData[1].SetProjects(projectData.Skip(8).ToList());

            projectData[0].SetSections(sectionData.Take(7).ToList());
            projectData[1].SetSections(sectionData.Skip(7).Take(5).ToList());
            projectData[2].SetSections(sectionData.Skip(7).Skip(5).ToList());

            siteRepository = new FakeSiteRepository();
            siteRepository.SetSites(siteData);
            siteRepository.SetProjects(projectData);
            siteRepository.SetSections(sectionData);

            userRepository = new FakeUserRepository();
            userRepository.Setup(x => x.GetByEmailAddress("test")).Returns(new FakeUser(1, "Test", "User", "test", "password").Object);
            
            httpContext = new FakeHttpContext();
            httpContext.Request.Setup(x => x.IsAuthenticated).Returns(true);
            httpContext.Identity.Setup(x => x.IsAuthenticated).Returns(true);
            httpContext.Identity.Setup(x => x.Name).Returns("test");

            controller = new SectionController(userRepository.Object, siteRepository.Object);
            httpContext.AttachToControllerContext(controller);
        }

        [TestMethod]
        public void SetctionDataTest()
        {
            Assert.AreEqual(15, sectionData.Count);
            for (int i = 0; i < sectionData.Count; i++)
            {
                var tmpProj = i >= 12 ? projectData[2].Object : i >= 7 ? projectData[1].Object : projectData[0].Object;
                var tmpSite = siteData[0].Object;
                Assert.AreEqual(tmpProj.Id, sectionData[i].Object.ProjectId);
                Assert.AreEqual(tmpProj, sectionData[i].Object.Project);
                Assert.IsTrue(tmpProj.Sections.Contains(sectionData[i].Object));

                Assert.AreEqual(tmpSite.Id, sectionData[i].Object.Project.SiteId);
                Assert.AreEqual(tmpSite, sectionData[i].Object.Project.Site);
            }
        }

        [TestMethod]
        public void SiteRepository_SetctionDataTest()
        {
            foreach (var section in sectionData.Convert())
            {
                Assert.AreEqual(section, siteRepository.Object.Section_GetById(section.Id));
            }
            foreach (var projects in sectionData.Convert().GroupBy(x => x.ProjectId))
            {
                Assert.AreEqual(projects.Count(), siteRepository.Object.Section_GetAllByProjectId(projects.Key).Count());
            }
        }

        [TestMethod]
        public void Index_Returns_ViewWithCorrectData()
        {
            var result = controller.Index(projectData[0].Object.Id);

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(List<Section>));

            var sections = (List<Section>)viewResult.ViewData.Model;

            Assert.AreEqual(projectData[0].Object.Sections.Count, sections.Count);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Details_InvalidId_Throws_NotImplementedException()
        {
            var result = controller.Details(siteData[0].Object, int.MaxValue);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Details_NullSite_Throws_NotImplementedException()
        {
            var result = controller.Details(null, sectionData[0].Object.Id);
        }

        [TestMethod]
        public void Details_ValidIds_Throws_ReturnsCorrectView()
        {
            var result = controller.Details(siteData[0].Object, sectionData[1].Object.Id);

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Section));

            var section = (Section)viewResult.ViewData.Model;

            Assert.AreEqual(sectionData[1].Object, section);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Edit_InvalidId_Throws_NotImplementedException()
        {
            var result = controller.Edit(siteData[0].Object, int.MaxValue);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Edit_NullSite_Throws_NotImplementedException()
        {
            var result = controller.Edit(null, sectionData[0].Object.Id);
        }

        [TestMethod]
        public void Edit_ValidIds_ReturnsCorrectView()
        {
            var result = controller.Edit(siteData[0].Object, sectionData[1].Object.Id);

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Section));

               var section = (Section)viewResult.ViewData.Model;

            Assert.AreEqual(sectionData[1].Object, section);
        }

        [TestMethod]
        public void Create_ValidIds_ReturnsCorrectView()
        {
            var result = controller.Create(siteData[0].Object, projectData[0].Object.Id);
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = (ViewResult)result;
            Assert.IsNull(viewResult.ViewData.Model);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Create_InValidProjectId_ThrowsNotImplimentedException()
        {
            var result = controller.Create(siteData[0].Object, int.MaxValue);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Create_NullSite_ThrowsNotImplimentedException()
        {
            var result = controller.Create(null, projectData[0].Object.Id);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Create_Post_InValidProjectId_ThrowsNotImplimentedException()
        {
            var result = controller.Create(siteData[0].Object, int.MaxValue, "Test");
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Create_Post_NullSite_ThrowsNotImplimentedException()
        {
            var result = controller.Create(null, projectData[0].Object.Id, "Test");
        }

        [TestMethod]
        public void Create_Post_DupplicationNameException_ReturnsCorrectView()
        {
            siteRepository.Setup(x => x.SaveChanges(Moq.It.IsAny<User>())).Throws(new DuplicateNameException());

            var result = controller.Create(siteData[0].Object, projectData[5].Object.Id, "NewName");

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Section));

            var section = (Section)viewResult.ViewData.Model;
            Assert.AreEqual("NewName", section.Name);
            Assert.AreEqual(projectData[5].Object.Id, section.ProjectId);

            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
            Assert.AreEqual(1, viewResult.ViewData.ModelState.Count);
            Assert.IsNotNull(viewResult.ViewData.ModelState["Name"]);
        }

        [TestMethod]
        public void Create_Post_ValidationErrors_ReturnsCorrectView()
        {
            siteRepository.Setup(x => x.SaveChanges(Moq.It.IsAny<User>())).Throws(new WhittleList.Bll.Exceptions.ValidationException("Rule Violation", new RuleViolation[] { new RuleViolation("Name", "Name is reuired"), new RuleViolation("ProjectId", "Project Id is reuired") }));

            var result = controller.Create(siteData[0].Object, projectData[5].Object.Id, "NewName");

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Section));

            var section = (Section)viewResult.ViewData.Model;
            Assert.AreEqual("NewName", section.Name);
            Assert.AreEqual(projectData[5].Object.Id, section.ProjectId);

            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
            Assert.AreEqual(2, viewResult.ViewData.ModelState.Count);
            Assert.IsNotNull(viewResult.ViewData.ModelState["Name"]);
            Assert.IsNotNull(viewResult.ViewData.ModelState["ProjectId"]);
        }

        [TestMethod]
        public void Create_Post_NoErrors_ReturnsCorrectView()
        {
            Section created = null;
            siteRepository.Setup(x => x.Insert(Moq.It.IsAny<Section>())).Callback<Section>((section) => created = section);
            var result = controller.Create(siteData[0].Object, projectData[5].Object.Id, "NewName");

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));

            var redirectResult = (RedirectToRouteResult)result;
            Assert.AreEqual("Details", redirectResult.RouteValues["action"]);
            Assert.AreEqual("Project", redirectResult.RouteValues["controller"]);
            Assert.AreEqual(projectData[5].Object.Id, redirectResult.RouteValues["id"]);

            siteRepository.Verify(x => x.Insert(Moq.It.IsAny<Section>()), Moq.Times.Once());
            siteRepository.Verify(x => x.SaveChanges(Moq.It.IsAny<User>()), Moq.Times.Once());

            Assert.IsNotNull(created.Name);
            Assert.AreEqual("NewName", created.Name);
            Assert.AreEqual(projectData[5].Object.Id, created.ProjectId);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Edit_Post_InValidId_ReturnsNotImplimeneted()
        {
            var result = controller.Edit(siteData[0].Object, int.MaxValue, "NewName", sectionData[5].Object.CID);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Edit_Post_NullSite_ReturnsNotImplimeneted()
        {
            var result = controller.Edit(null, sectionData[5].Object.Id, "NewName", sectionData[5].Object.CID);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void Edit_Post_ConcurencyException_ReturnsNotImplimeneted()
        {
            var result = controller.Edit(siteData[0].Object, sectionData[5].Object.Id, "NewName", MockHelpers.CreateBinary(rnd));
        }

        [TestMethod]
        public void Edit_Post_DuplicateNameException_ReturnsCorrectView()
        {
            siteRepository.Setup(x => x.SaveChanges(Moq.It.IsAny<User>())).Throws(new DuplicateNameException());

            var result = controller.Edit(siteData[0].Object, sectionData[5].Object.Id, "NewName", sectionData[5].Object.CID);
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Section));

            var section = (Section)viewResult.ViewData.Model;
            Assert.AreEqual("NewName", section.Name);
            Assert.AreEqual(sectionData[5].Object.ProjectId, section.ProjectId);

            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
            Assert.AreEqual(1, viewResult.ViewData.ModelState.Count);
            Assert.IsNotNull(viewResult.ViewData.ModelState["Name"]);
        }

        [TestMethod]
        public void Edit_Post_ValidationException_ReturnsCorrectView()
        {
            siteRepository.Setup(x => x.SaveChanges(Moq.It.IsAny<User>())).Throws(new WhittleList.Bll.Exceptions.ValidationException("Rule Violation", new RuleViolation[] { new RuleViolation("Name", "Name is reuired"), new RuleViolation("ProjectId", "Project Id is reuired") }));

            var result = controller.Edit(siteData[0].Object, sectionData[5].Object.Id, "NewName", sectionData[5].Object.CID);
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));

            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Section));

            var section = (Section)viewResult.ViewData.Model;
            Assert.AreEqual("NewName", section.Name);
            Assert.AreEqual(sectionData[5].Object.ProjectId, section.ProjectId);

            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
            Assert.AreEqual(2, viewResult.ViewData.ModelState.Count);
            Assert.IsNotNull(viewResult.ViewData.ModelState["Name"]);
            Assert.IsNotNull(viewResult.ViewData.ModelState["ProjectId"]);
        }

        [TestMethod]
        public void Edit_Post_NoException_ReturnsCorrectView()
        {
            var result = controller.Edit(siteData[0].Object, sectionData[5].Object.Id, "NewName", sectionData[5].Object.CID);
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));

            var redirectResult = (RedirectToRouteResult)result;
            Assert.AreEqual("Index", redirectResult.RouteValues["action"]);

            siteRepository.Verify(x => x.SaveChanges(Moq.It.IsAny<User>()), Moq.Times.Once());

            Assert.AreEqual("NewName", sectionData[5].Object.Name);
        }



    }
}
